#include "axbopenglwidget.h"
#include <QtDebug>
// 创建 VBO VAO 对象， 并赋予 ID
unsigned int VBOs[2], VAOs[2];
float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f,  -0.5f, 0.0f,
    0.0f,   0.5f, 0.0f
};

float firstTriangle[] = {
    -0.9f, -0.5f, 0.0f, // left
    -0.0f, -0.5f, 0.0f, // right
    -0.45f, 0.5f, 0.0f // top
};

float secondTriangle[] = {
    0.0f, -0.5f, 0.0f, //left
    0.9f, -0.5f, 0.0f, // right
    0.45f, 0.5f, 0.0f // top
};

const char *vertexShaderSource = "#version 330 core\n" \
        "layout (location = 0) in vec3 aPos;\n" \
        "void main() {\n" \
        "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); \n" \
        "}\0";

const char* fragmentShaderSourceFirst = "#version 330 core\n" \
        "out vec4 FragColor;\n" \
        "void main() {\n" \
        " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" \
        "}\n\0";

const char* fragmentShaderSourceSecond = "#version 330 core\n" \
        "out vec4 FragColor;\n" \
        "void main() {\n" \
        " FragColor = vec4(0.5f, 0.0f, 1.0f, 0.5f);\n" \
        "}\n\0";


AXBOpenGLWidget::AXBOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{

}

void AXBOpenGLWidget::initializeGL()
{
    // 初始化 opengl 中的函数指针
    initializeOpenGLFunctions();
    initVaoVboEbo(&VAOs[0], &VBOs[0], firstTriangle, sizeof(firstTriangle)/sizeof(firstTriangle[0]));
    initVaoVboEbo(&VAOs[1], &VBOs[1], secondTriangle, sizeof(secondTriangle)/sizeof(secondTriangle[0]));

    initShader(&shaderProgram_1, vertexShaderSource, fragmentShaderSourceFirst);
    initShader(&shaderProgram_2, vertexShaderSource, fragmentShaderSourceSecond);
}

void AXBOpenGLWidget::resizeGL(int w, int h)
{
//    glViewport(0, 0, w, h);
}

void AXBOpenGLWidget::paintGL()
{
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清理时的颜色
    glClear(GL_COLOR_BUFFER_BIT); // 使用

    glUseProgram(shaderProgram_1);
    // 此时要画图了， VAO 小助理，你要再次被用
    glBindVertexArray(VAOs[0]);
    glDrawArrays(GL_TRIANGLES, 0, 3); // 画一个三角形

    glUseProgram(shaderProgram_2);
    glBindVertexArray(VAOs[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3); // 画一个三角形

}

int AXBOpenGLWidget::initVaoVboEbo(unsigned int *vao, unsigned int *vbo, float *vertices, int len_vertices)
{
    glGenVertexArrays(1, vao);
    glGenBuffers(1, vbo);

    // 绑定 VBO 和 VAO 对象
    glBindVertexArray(*vao); // 告诉 VAO 小助理，现在开始记了
    glBindBuffer(GL_ARRAY_BUFFER, *vbo); // VBO 也是一样的

    // 为当前绑定到 target 的缓冲区对象创建一个新的数据存储.（是在显存中分配缓冲）
    // 如果 data 不是 NULL, 则使用 来自此指针的数据 初始化数据存储
    glBufferData(GL_ARRAY_BUFFER, len_vertices * sizeof(float), vertices, GL_STATIC_DRAW);

    // 告知 显卡如何解析缓冲里的属性值 , 让 VAO 小助理，记录 如何解析缓冲中的内容
    // 第 0 个属性， 里面有三个值(x,y,z), 每个值是一个 Float类型，不需要标准化，步长为三个float 大小了， 偏移为0
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void *)0);

    // 开启 VAO 管理的第一个属性
    glEnableVertexAttribArray(0);

    // VAO VBO 两个助理记录完，这些信息，后让其休息 （解绑）
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

int AXBOpenGLWidget::initShader(unsigned int *shaderProgram, const char *vertexShaderSource, const char *fragmentShaderSource)
{
    // 顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); // 创建一个顶点着色器
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // 顶点着色器和 对应的源码，进行绑定
    glCompileShader(vertexShader); // 编译顶点着色器
    // 检查编译是否成功
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED  -- " << infoLog << "\n";
    }


    // 片源 着色器/ 片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader); // 编译顶点着色器
    // 检查编译是否成功
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        qDebug() << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED  -- " << infoLog << "\n";
    }


    //链接 着色器
    *shaderProgram = glCreateProgram();
    glAttachShader(*shaderProgram, vertexShader); // 将顶点着色器加入 program中
    glAttachShader(*shaderProgram, fragmentShader); // 将 片源着色器加入 program 中
    glLinkProgram(*shaderProgram); // 链接编译
    // check for linking errors
    glGetProgramiv(*shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(*shaderProgram, 512, NULL, infoLog);
        qDebug() << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
}
